using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.VisualStudio.Package;
using Microsoft.WowAddonStudio.Lua.LanguageService.Support;
using Microsoft.WowAddonStudio.Lua.Parser;

namespace Microsoft.WowAddonStudio.Lua.LanguageService.Model
{
    public class LuaAuthoringSink : AuthoringSink, IParserSink
    {
        private readonly List<IParserSink> parserSinks = new List<IParserSink>(1);

        /// <summary>
        /// Initializes a new instance of the <see cref="LuaAuthoringSink"/> class.
        /// </summary>
        /// <param name="reason">The parse reason.</param>
        /// <param name="line">The line.</param>
        /// <param name="col">The column.</param>
        /// <param name="maxErrors">The maximum number of errors.</param>
        public LuaAuthoringSink(ParseReason reason, int line, int col, int maxErrors) : base(reason, line, col, maxErrors)
        {
        }

        public void AddParserSink(IParserSink sink)
        {
            if (sink == null)
                throw new ArgumentNullException("sink");

            parserSinks.Add(sink);
        }

        public void EnsureParserSink<TParserSink>() where TParserSink : IParserSink, new()
        {
            if (Equals(FindParserSink<TParserSink>(), default(TParserSink)))
                parserSinks.Add(new TParserSink());
        }

        public TParserSink FindParserSink<TParserSink>() where TParserSink : IParserSink
        {
            return parserSinks.OfType<TParserSink>().SingleOrDefault();
        }

        #region IParserSink Explicit Implementation

        void IParserSink.AddWarning(LexLocation location, string message)
        {
            AddError(String.Empty, message, location.ToTextSpan(), Severity.Warning);
        }

        void IParserSink.AddError(LexLocation location, string message)
        {
            AddError(String.Empty, message, location.ToTextSpan(), Severity.Error);
        }

        void IParserSink.MatchPair(LexLocation left, LexLocation right)
        {
            MatchPair(left.ToTextSpan(), right.ToTextSpan(), 1);
        }

        void IParserSink.AddHiddenRegion(LexLocation location)
        {
            AddHiddenRegion(location.ToTextSpan());
        }

        void IParserSink.StartName(LexLocation location, string name)
        {
            StartName(location.ToTextSpan(), name);

            foreach (var parserSink in parserSinks)
                parserSink.StartName(location, name);
        }

        void IParserSink.QualifyName(LexLocation selectorLocation, LexLocation nameLocation, string name)
        {
            QualifyName(selectorLocation.ToTextSpan(), nameLocation.ToTextSpan(), name);

            foreach (var parserSink in parserSinks)
                parserSink.QualifyName(selectorLocation, nameLocation, name);
        }

        void IParserSink.StartParameters(LexLocation location)
        {
            StartParameters(location.ToTextSpan());
        }

        void IParserSink.NextParameter(LexLocation location)
        {
            NextParameter(location.ToTextSpan());
        }

        void IParserSink.EndParameters(LexLocation location)
        {
            EndParameters(location.ToTextSpan());
        }

        #endregion
    }
}